package com.java.jvalidator.framework.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.java.jvalidator.framework.TypeUtil;
import com.java.jvalidator.framework.ValidationException;
import com.java.jvalidator.framework.ValidationExceptionMsg;
import com.java.jvalidator.framework.XMLConfigConst;
import com.java.jvalidator.framework.validator.AbstractCustomValidator;
import com.java.jvalidator.framework.validator.AbstractValidator;
import com.java.jvalidator.framework.validator.CompareValidator;
import com.java.jvalidator.framework.validator.EnumValidator;
import com.java.jvalidator.framework.validator.NotEmptyValidator;
import com.java.jvalidator.framework.validator.RangeValidator;
import com.java.jvalidator.framework.validator.RegExValidator;

class DOMParser implements XMLConfigFileParser {
	private static final String LEFT_BRACE = "{";
	private static final String RIGHT_BRACE = "}";
	private static final String DOT = ".";
	private static final String FILE_SEPERATOR = "/";
	private static final String EMPTY_STRING = "";
	private static List<String> allowComparison = new ArrayList<String>();
	private static List<String> allowSimpleRuleName = new ArrayList<String>();
	private DocumentBuilder domBuilder;

	static {
		allowComparison.add(XMLConfigConst.EQUAL);// equal
		allowComparison.add(XMLConfigConst.NOT_EQUAL);// not equal
		allowComparison.add(XMLConfigConst.GREATER_EQUAL);// 大于等于
		allowComparison.add(XMLConfigConst.GREATER_THAN);// 大于
		allowComparison.add(XMLConfigConst.LESS_EQUAL);// 小于等于
		allowComparison.add(XMLConfigConst.LESS_THAN);// 小于

		allowSimpleRuleName.add(XMLConfigConst.NOT_EMPTY);// 非空
		allowSimpleRuleName.add(XMLConfigConst.RANGE);// 范围
		allowSimpleRuleName.add(XMLConfigConst.COMPARE);// compare
		allowSimpleRuleName.add(XMLConfigConst.REGEX);// 正则表达式
		allowSimpleRuleName.add(XMLConfigConst.ENUM);// 枚举
		allowSimpleRuleName.add(XMLConfigConst.CUSTOM);// 自定义
	}

	public ClassConfig parseClassConfig(String className) throws ValidationException {
		if (className == null) {
			throw new ValidationException(ValidationExceptionMsg.VALIDATION_ERROR);
		}

		// 读取该Class对应的校验XML文件，返回节点
		Node classNode = parseClassNode(className);
		NamedNodeMap classNodeAttributes = classNode.getAttributes();
		Node nameAttr = classNodeAttributes.getNamedItem(XMLConfigConst.NAME);
		if (nameAttr == null) {
			throw new ValidationException(ValidationExceptionMsg.CLASS_HAS_NO_NAME_ERROR);
		}
		// 取得Class tag的name属性
		String targetClassName = nameAttr.getNodeValue();
		
		// 传入的className必须和XML中解析出来的匹配
		if ((TypeUtil.isStringNullOrEmpty(targetClassName)) || (!targetClassName.equals(className))) {
			throw new ValidationException(ValidationExceptionMsg.CLASS_NAME_ATTRIBUTE_ERROR);
		}
		
		// 取得class node下的子节点
		NodeList propertyNodes = classNode.getChildNodes();
		ClassConfig classConfig = new ClassConfig(targetClassName);
		for (int nodeIndex = 0; nodeIndex < propertyNodes.getLength(); nodeIndex++) {
			Node propertyNode = propertyNodes.item(nodeIndex);
			// 取得property配置
			if (XMLConfigConst.PROPERTY.equals(propertyNode.getNodeName())) {
				PropertyConfig fieldConfig = parseProperty(propertyNode, targetClassName);
				classConfig.addFieldConfig(fieldConfig);
			}
		}
		return classConfig;
	}

	public Map<String, RuleComposite> parseRuleComposite() throws ValidationException {
		InputStream fileInputStream = getClass().getClassLoader().getResourceAsStream(XMLConfigConst.RULE_COMPOSITE_FILE);

		if (fileInputStream == null) {
			return null;
		}
		Document doc = getXMLFileDocument(fileInputStream);

		if (doc.getFirstChild() == null) {
			return null;
		}

		NodeList nodeList = doc.getFirstChild().getChildNodes();
		Map<String, RuleComposite> composites = new HashMap<String, RuleComposite>();
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node node = nodeList.item(i);

			if (XMLConfigConst.COMPOSITE.equals(node.getNodeName())) {
				RuleComposite ruleComposite = parseCompositeTag(node, composites);
				if (ruleComposite != null) {
					composites.put(ruleComposite.getId(), ruleComposite);
				}
			}
		}
		return composites;
	}

	private RuleComposite parseCompositeTag(Node compositeNode, Map<String, RuleComposite> parsedComposites) throws ValidationException {
		if (compositeNode == null) {
			return null;
		}
		if (!XMLConfigConst.COMPOSITE.equals(compositeNode.getNodeName())) {
			throw new ValidationException("The name of the element must be 'composite'!");
		}
		NamedNodeMap attriMap = compositeNode.getAttributes();
		Node nameNode = attriMap.getNamedItem(XMLConfigConst.ID);
		if (nameNode == null) {
			throw new ValidationException("The composite element must have a property called 'id'");
		}
		RuleComposite comp = new RuleComposite(nameNode.getNodeValue());
		NodeList nodeList = compositeNode.getChildNodes();
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node ruleNode = nodeList.item(i);
			String ruleName = TypeUtil.trimString(ruleNode.getNodeName());
			boolean predefinedRule = allowSimpleRuleName.contains(ruleName);
			if (predefinedRule) {
				comp.addRule(parseSimpleRuleConfig(ruleNode, EMPTY_STRING, EMPTY_STRING));
			} else if (XMLConfigConst.COMPOSITE.equals(ruleName)) {
				NamedNodeMap attrMap = ruleNode.getAttributes();
				Node nameAttr = attrMap.getNamedItem("id");
				if (nameAttr == null) {
					throw new ValidationException("The composite element must have a property called 'id'");
				}
				String referencedId = nameAttr.getNodeValue();
				if (!parsedComposites.keySet().contains(referencedId)) {
					throw new ValidationException("The composite rule element doesn't exist for the name:" + referencedId);
				}
				RuleComposite referencedComp = (RuleComposite) parsedComposites.get(referencedId);
				comp.addRules(referencedComp.getRules());
			}
		}
		return comp;
	}

	private PropertyConfig parseProperty(Node propertyTag, String className) throws ValidationException {
		NamedNodeMap propertyAttribute = propertyTag.getAttributes();
		if (propertyAttribute == null) {
			throw new ValidationException("The property tag must have a name attribute");
		}
		Node nameAttribute = propertyAttribute.getNamedItem("name");
		if (nameAttribute == null) {
			throw new ValidationException("The property tag must have a name attribute");
		}
		String propertyName = nameAttribute.getNodeValue();
		if (TypeUtil.isStringNullOrEmpty(propertyName)) {
			throw new ValidationException("Property name mustn't be empty!");
		}
		PropertyConfig propertyConfig = new PropertyConfig(className, propertyName);
		NodeList rulesNode = propertyTag.getChildNodes();
		if (rulesNode.getLength() == 0) {
			return propertyConfig;
		}

		for (int nodeIndex = 0; nodeIndex < rulesNode.getLength(); nodeIndex++) {
			Node ruleNode = rulesNode.item(nodeIndex);
			if (ruleNode != null) {
				String ruleName = TypeUtil.trimString(ruleNode.getNodeName());
				boolean predefinedRule = allowSimpleRuleName.contains(ruleName);
				if (predefinedRule) {
					RuleConfig ruleConfig = parseSimpleRuleConfig(ruleNode, className, propertyName);
					propertyConfig.addRule(ruleConfig);
				}
				if (XMLConfigConst.COMPOSITE.equals(ruleName)) {
					List<RuleConfig> rules = resolveRuleComposite(ruleNode, className, propertyName);
					for (RuleConfig rule : rules) {
						propertyConfig.addRule(rule);
					}
				}
			}
		}
		return propertyConfig;
	}

	private RuleConfig parseSimpleRuleConfig(Node ruleNode, String className, String propertyName) throws ValidationException {
		String ruleName = TypeUtil.trimString(ruleNode.getNodeName());
		NamedNodeMap ruleAttr = ruleNode.getAttributes();
		Node errorMsgAttr = ruleAttr.getNamedItem("errorMsg");
		String errorMsgName = EMPTY_STRING;
		if (errorMsgAttr != null) {
			errorMsgName = TypeUtil.trimString(errorMsgAttr.getNodeValue());
		}

		if (!"custom".equals(ruleNode.getNodeName())) {
			Node node = ruleNode.getAttributes().getNamedItem("errorMsg");
			if (node == null) {
				throw new ValidationException("This attribute is required:errorMsg");
			}

		}

		RuleConfig ruleConfig = null;

		if ("compare".equals(ruleName)) {
			ruleConfig = parseRuleCompare(ruleNode, className, propertyName, errorMsgName);
		} else if ("custom".equals(ruleName)) {
			ruleConfig = parseRuleCustom(ruleNode, className, propertyName, errorMsgName);
		} else if ("enum".equals(ruleName)) {
			ruleConfig = parseRuleEnum(ruleNode, className, propertyName, errorMsgName);
		} else if ("notEmpty".equals(ruleName)) {
			ruleConfig = new RuleConfig(className, propertyName, NotEmptyValidator.getInstance(), null, errorMsgName);
		} else if ("range".equals(ruleName)) {
			ruleConfig = parseRuleRange(ruleNode, className, propertyName, errorMsgName);
		} else if ("regex".equals(ruleName))
			ruleConfig = parseRuleRegEx(ruleNode, className, propertyName, errorMsgName);
		else {
			return null;
		}
		NodeList children = ruleNode.getChildNodes();
		Map<String, String> namedPlaceHolder = new HashMap<String, String>();
		for (int i = 0; i < children.getLength(); i++) {
			Node child = children.item(i);
			if ("placeHolder".equals(child.getNodeName())) {
				namedPlaceHolder.putAll(parseNamedPlaceHolder(child));
			}
		}
		ruleConfig.addParameters(parseConditionParameter(ruleNode));
		ruleConfig.setNamedPlaceHolder(namedPlaceHolder);
		NamedNodeMap ruleAttribute = ruleNode.getAttributes();
		Node multipleErrorAttr = ruleAttribute.getNamedItem("multipleError");
		if ((multipleErrorAttr != null) && ("false".equals(multipleErrorAttr.getNodeValue()))) {
			ruleConfig.setMultiError(false);
		}

		return ruleConfig;
	}

	private List<RuleConfig> resolveRuleComposite(Node compositeNode, String className, String fieldName) throws ValidationException {
		if ((compositeNode == null) || (className == null) || (fieldName == null)) {
			return null;
		}
		NamedNodeMap attrMap = compositeNode.getAttributes();
		Node idAttr = attrMap.getNamedItem("id");
		if (idAttr == null) {
			throw new ValidationException("The composite element must have a property called 'id'");
		}
		String compId = idAttr.getNodeValue();
		RuleComposite ruleComposite = ValidationConfig.getInstance().getRuleComposite(compId);
		if (ruleComposite == null) {
			throw new ValidationException("The composite rule element doesn't exist for the name:" + compId);
		}

		NodeList children = compositeNode.getChildNodes();
		Parameters param = new Parameters();
		Map<String, String> namedPlaceHolder = new HashMap<String, String>();
		for (int i = 0; i < children.getLength(); i++) {
			Node childNode = children.item(i);

			if ("parameter".equals(childNode.getNodeName())) {
				param.addParameters(parseParameter(childNode));
			}

			if ("placeHolder".equals(childNode.getNodeName())) {
				namedPlaceHolder.putAll(parseNamedPlaceHolder(childNode));
			}
		}
		Parameters conditionParam = parseConditionParameter(compositeNode);

		List<RuleConfig> compositeRules = ruleComposite.getRules();
		List<RuleConfig> resultRules = new ArrayList<RuleConfig>();

		Set<String> paramNames = param.getParameterNames();
		for (RuleConfig rule : compositeRules) {
			RuleConfig cloneRule = (RuleConfig) rule.clone();
			cloneRule.setTargetClassName(className);
			cloneRule.setFieldName(fieldName);
			String conditionValue;
			if (conditionParam != null) {
				String conditionProperty = conditionParam.getParameterValueAsString("conditionProperty");
				conditionValue = conditionParam.getParameterValueAsString("conditionValue");
				cloneRule.getParameter().addCondition(conditionProperty, conditionValue);
			}

			for (String paramName : paramNames) {
				cloneRule.getParameter().replaceParameterValue(LEFT_BRACE + paramName + RIGHT_BRACE, param.getParameterValue(paramName));
			}

			String errorMsgName = cloneRule.getErrorMsgName();
			boolean errorMsgIsReplaceable = (!TypeUtil.isStringNullOrEmpty(errorMsgName)) && (errorMsgName.startsWith(LEFT_BRACE)) && (errorMsgName.endsWith(RIGHT_BRACE)) && (errorMsgName.length() > 3);
			if (errorMsgIsReplaceable) {
				String paramName = errorMsgName.substring(1, errorMsgName.length() - 1);
				if (param.hasParameter(paramName)) {
					cloneRule.setErrorMsgName(param.getParameterValueAsString(paramName));
				}
			}
			cloneRule.setNamedPlaceHolder(namedPlaceHolder);
			resultRules.add(cloneRule);
		}
		return resultRules;
	}

	private Map<String, String> parseNamedPlaceHolder(Node holderTag) throws ValidationException {
		NamedNodeMap attrMap = holderTag.getAttributes();
		Node nameAttr = attrMap.getNamedItem("name");
		Node valueAttr = attrMap.getNamedItem("value");
		if ((nameAttr == null) || (valueAttr == null)) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}
		String name = nameAttr.getNodeValue();
		String value = valueAttr.getNodeValue();
		if ((TypeUtil.isStringNullOrEmpty(name)) || (TypeUtil.isStringNullOrEmpty(value))) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}
		Map<String, String> namedPlaceHolder = new HashMap<String, String>();
		namedPlaceHolder.put(name, value);
		return namedPlaceHolder;
	}

	private Parameters parseConditionParameter(Node ruleNode) throws ValidationException {
		NamedNodeMap ruleAttr = ruleNode.getAttributes();
		Node conditionProperty = ruleAttr.getNamedItem("conditionProperty");

		if ((conditionProperty == null) || (TypeUtil.isStringNullOrEmpty(conditionProperty.getNodeValue()))) {
			return null;
		}

		Node conditionValue = ruleAttr.getNamedItem("conditionValue");
		if ((conditionValue == null) || (TypeUtil.isStringNullOrEmpty(conditionValue.getNodeValue()))) {
			throw new ValidationException("This attribute is required:conditionValue");
		}

		if (!checkConditionParameter(conditionProperty.getNodeValue(), conditionValue.getNodeValue())) {
			throw new ValidationException("There are some errors occured at condition parameters");
		}
		Parameters param = new Parameters();
		param.addParameter("conditionProperty", TypeUtil.trimString(conditionProperty.getNodeValue()));
		param.addParameter("conditionValue", TypeUtil.trimString(conditionValue.getNodeValue()));
		return param;
	}

	private boolean checkConditionParameter(String conditionProperty, String conditionValue) {
		return (!conditionProperty.startsWith(",")) && (!conditionProperty.endsWith(",")) && (!conditionValue.startsWith(",")) && (!conditionValue.endsWith(",")) && (conditionProperty.split(",").length == conditionValue.split(",").length);
	}

	private RuleConfig parseRuleRegEx(Node ruleNode, String className, String fieldName, String errorMsg) throws ValidationException {
		NamedNodeMap ruleAttr = ruleNode.getAttributes();

		Node patternAttr = ruleAttr.getNamedItem(XMLConfigConst.PATTERN);

		if ((patternAttr == null) || (TypeUtil.isStringNullOrEmpty(patternAttr.getNodeValue()))) {
			throw new ValidationException("This attribute is required:pattern");
		}

		String patternValue = TypeUtil.trimString(patternAttr.getNodeValue());
		Parameters param = new Parameters();
		param.addParameter(XMLConfigConst.PATTERN, patternValue);

		Node matchAttr = ruleAttr.getNamedItem("match");
		if (matchAttr != null) {
			String matchValue = TypeUtil.trimString(matchAttr.getNodeValue());
			if ((!"true".equals(matchValue)) && (!"false".equals(matchValue))) {
				throw new ValidationException("match value must be true or false!");
			}
			param.addParameter("match", matchValue);
		}

		RuleConfig ruleConfig = new RuleConfig(className, fieldName, RegExValidator.getInstance(), param, errorMsg);
		return ruleConfig;
	}

	private RuleConfig parseRuleRange(Node ruleNode, String className, String fieldName, String errorMsg) throws ValidationException {
		NamedNodeMap ruleAttr = ruleNode.getAttributes();
		Node minAttr = ruleAttr.getNamedItem("min");
		Node maxAttr = ruleAttr.getNamedItem("max");
		if ((minAttr == null) && (maxAttr == null)) {
			throw new ValidationException("min and max attribute must have one at least!");
		}
		String minValue = null;
		if (minAttr != null) {
			minValue = TypeUtil.trimString(minAttr.getNodeValue());
		}
		String maxValue = null;
		if (maxAttr != null) {
			maxValue = TypeUtil.trimString(maxAttr.getNodeValue());
		}
		if ((TypeUtil.isStringNullOrEmpty(minValue)) && (TypeUtil.isStringNullOrEmpty(maxValue))) {
			throw new ValidationException("min and max attribute must have one at least!");
		}
		Parameters param = new Parameters();
		if (!TypeUtil.isStringNullOrEmpty(minValue)) {
			param.addParameter("min", minValue);
		}
		if (!TypeUtil.isStringNullOrEmpty(maxValue)) {
			param.addParameter("max", maxValue);
		}

		parseDateFormatAttribute(ruleAttr, param);

		RuleConfig ruleConfig = new RuleConfig(className, fieldName, RangeValidator.getInstance(), param, errorMsg);
		return ruleConfig;
	}

	private RuleConfig parseRuleEnum(Node ruleNode, String className, String fieldName, String errorMsg) throws ValidationException {
		NamedNodeMap ruleAttr = ruleNode.getAttributes();

		Node valuesAttr = ruleAttr.getNamedItem("values");
		if ((valuesAttr == null) || (TypeUtil.isStringNullOrEmpty(valuesAttr.getNodeValue()))) {
			throw new ValidationException("This attribute is required:values");
		}

		Parameters param = new Parameters();
		param.addParameter("values", TypeUtil.trimString(valuesAttr.getNodeValue()));
		Node containsAttr = ruleAttr.getNamedItem("contains");

		if ((containsAttr == null) || (TypeUtil.isStringNullOrEmpty(containsAttr.getNodeValue()))) {
			param.addParameter("contains", "true");
		} else {
			String contains = TypeUtil.trimString(containsAttr.getNodeValue()).toLowerCase();
			if ((!"true".equalsIgnoreCase(contains)) && (!"false".equalsIgnoreCase(contains))) {
				throw new ValidationException("contains value must be true or false!");
			}
			param.addParameter("contains", contains);
		}

		parseDateFormatAttribute(ruleAttr, param);

		RuleConfig ruleConfig = new RuleConfig(className, fieldName, EnumValidator.getInstance(), param, errorMsg);
		return ruleConfig;
	}

	private void parseDateFormatAttribute(NamedNodeMap ruleAttr, Parameters param) {
		Node dateFormatAttr = ruleAttr.getNamedItem("dateFormat");
		if ((dateFormatAttr != null) && (!TypeUtil.isStringNullOrEmpty(dateFormatAttr.getNodeValue())))
			param.addParameter("dateFormat", dateFormatAttr.getNodeValue());
	}

	private RuleConfig parseRuleCustom(Node ruleNode, String className, String fieldName, String errorMsg) throws ValidationException {
		NamedNodeMap nodeAttr = ruleNode.getAttributes();
		if (nodeAttr == null) {
			throw new ValidationException("custom attribute error!");
		}
		Node classAttr = nodeAttr.getNamedItem("class");

		if ((classAttr == null) || (TypeUtil.isStringNullOrEmpty(classAttr.getNodeValue()))) {
			throw new ValidationException("This attribute is required:class");
		}

		String customValidatorClassName = classAttr.getNodeValue();
		AbstractValidator validator = null;
		try {
			Class<?> validatorClass = Class.forName(customValidatorClassName);
			validator = (AbstractCustomValidator) validatorClass.newInstance();
		} catch (ClassNotFoundException e) {
			throw new ValidationException(e);
		} catch (InstantiationException e) {
			throw new ValidationException(e);
		} catch (IllegalAccessException e) {
			throw new ValidationException(e);
		}
		Parameters params = new Parameters();
		NodeList parameterTags = ruleNode.getChildNodes();
		for (int nodeIndex = 0; nodeIndex < parameterTags.getLength(); nodeIndex++) {
			Node parameterTag = parameterTags.item(nodeIndex);
			if ("parameter".equals(parameterTag.getNodeName())) {
				params.addParameters(parseParameter(parameterTag));
			}
		}
		RuleConfig ruleConfig = new RuleConfig(className, fieldName, validator, params, errorMsg);
		return ruleConfig;
	}

	private Parameters parseParameter(Node parameterTag) throws ValidationException {
		NamedNodeMap parameterAttr = parameterTag.getAttributes();
		Node nameAttr = parameterAttr.getNamedItem("name");
		Node valueAttr = parameterAttr.getNamedItem("value");
		if ((nameAttr == null) || (valueAttr == null)) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}
		String name = nameAttr.getNodeValue();
		String value = valueAttr.getNodeValue();
		if ((TypeUtil.isStringNullOrEmpty(name)) || (TypeUtil.isStringNullOrEmpty(value))) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}
		Parameters parameter = new Parameters();
		parameter.addParameter(name, value);
		return parameter;
	}

	private RuleConfig parseRuleCompare(Node ruleNode, String className, String fieldName, String errorMsg) throws ValidationException {
		NamedNodeMap ruleAttr = ruleNode.getAttributes();

		Node compareWithAttr = ruleAttr.getNamedItem("compareWith");

		if ((compareWithAttr == null) || (TypeUtil.isStringNullOrEmpty(compareWithAttr.getNodeValue()))) {
			throw new ValidationException("This attribute is required:compareWith");
		}

		String compareWith = TypeUtil.trimString(compareWithAttr.getNodeValue());
		Parameters param = new Parameters();
		param.addParameter("compareWith", compareWith);

		Node comparsionAttr = ruleAttr.getNamedItem("comparison");
		if (comparsionAttr != null) {
			String comparsion = comparsionAttr.getNodeValue();
			if ((!TypeUtil.isStringNullOrEmpty(comparsion)) && (!allowComparison.contains(comparsion))) {
				throw new ValidationException("comparison must be eq,ne,gt,lt,ge or le!");
			}
		}
		String comparison = null;
		if (comparsionAttr == null)
			comparison = "eq";
		else {
			comparison = TypeUtil.trimString(comparsionAttr.getNodeValue()).toLowerCase();
		}
		param.addParameter("comparison", comparison);

		RuleConfig ruleConfig = new RuleConfig(className, fieldName, CompareValidator.getInstance(), param, errorMsg);
		return ruleConfig;
	}

	/****
	 * 通过class名，找到对应的xml文件,并返回Class Node.
	 * <p>
	 * 例如，传入"demo.Person"，则找demo/Person_jValidator.xml文件，然后返回class <class name="demo.Person"></class> 节点
	 * </p>
	 * 
	 * @param className
	 * @return
	 * @throws ValidationException
	 */
	private Node parseClassNode(String className) throws ValidationException {
		String filePath = className.replace(DOT, FILE_SEPERATOR);
		filePath = filePath + XMLConfigConst.CONFIG_FILE_SUFFIX;
		InputStream fileInputStream = getClass().getClassLoader().getResourceAsStream(filePath);
		if (fileInputStream == null) {
			throw new ValidationException(ValidationExceptionMsg.CONFIG_FILE_NOT_EXIST_ERROR + className);
		}
		Document fileDom = getXMLFileDocument(fileInputStream);
		NodeList nodes = fileDom.getElementsByTagName(XMLConfigConst.CLASS);
		if ((nodes == null) || (nodes.getLength() != 1)) {
			// 一个XML文件中只能配置一个Class的校验规则
			throw new ValidationException(ValidationExceptionMsg.TOO_MUCH_ROOT_ELEMENT_ERROR);
		}
		return nodes.item(0);
	}

	/***
	 * 根据InputStream,返回document对象
	 * 
	 * @param iStream
	 * @return
	 * @throws ValidationException
	 */
	Document getXMLFileDocument(InputStream iStream) throws ValidationException {
		if (this.domBuilder == null) {
			try {
				this.domBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			} catch (ParserConfigurationException e) {
				throw new ValidationException(ValidationExceptionMsg.DOM_PARSER_CREATE_ERROR);
			}
		}
		Document fileDom = null;
		try {
			fileDom = this.domBuilder.parse(iStream);
		} catch (SAXException e) {
			throw new ValidationException(e);
		} catch (IOException e) {
			throw new ValidationException(e);
		}
		try {
			iStream.close();
		} catch (IOException e) {
			throw new ValidationException(e);
		}
		return fileDom;
	}
}
